#!/usr/bin/perl

###############################################################################
# A script to list/download project files from googlecode
#
# Requires curl binary (http://curl.haxx.se/)
#
# muquit@muquit.com Sep-08-2007 - first cut
###############################################################################

use strict;
use Getopt::Long;
$|=1;

## --- globals.  -starts-
my $me=$0;
$me =~ s!.*/!!;

my $g_version_s="1.03";
my $g_curl_path="curl";
my $g_domain="code.google.com";
my $g_loc="/files";
my ($g_project,$g_user,$g_pass)='';
my $g_version=0;
my $g_debug=0;
my $g_fetch_prompt=0;
my $g_fetch=0;
my $g_show_progress=0;
my $g_help=0;

my %g_options=(
    "project=s"     =>\$g_project,
    "progress"      =>\$g_show_progress,
    "getprompt"   =>\$g_fetch_prompt,
    "get=s"     => \$g_fetch,
    "help"      =>\$g_help,
    "version"   =>\$g_version,
    "debug"     =>\$g_debug,
    );
## --- globals.  -ends-

&doit();
# won't be here

sub run_cmd()
{
    my $cmd=shift;
    local *FD;
    &print_debug("cmd: $cmd");
    open(FD,"$cmd |") or &print_fatal("could not run $cmd");
    my @lines=(<FD>);
    close(FD);
    return(\@lines);

}

sub ask()
{
    my $prompt=shift;
    for (;;)
    {
        print "$prompt";
        my $ans=(<>);
        chomp($ans);
        next if (! $ans);
        &print_debug("Ans: '$ans'");
        if ($ans =~ /^y.*$/i)
        {
            return(1);
        }
        elsif ($ans =~/^n.*$/i)
        {
            return(0);
        }
        elsif ($ans =~ /^q.*$/i)
        {
            exit(0);
        }
        else
        {
            next;
        }
    }
}

sub fetch_file()
{
    my $curl=shift;
    my $file=shift;
    my $base_url=shift;
    my $prompt=shift;
    my $url="${base_url}/${file}";
    my $rc = 1;
    if ($prompt)
    {
        $rc=&ask("Download file: '$file' [y|n|q]? ");
    }
    else
    {
        print "Downloading: $file\n";
    }
    if ($rc)
    {
        my $cmd="$curl -O ${url}";
        my $lref=&run_cmd("$cmd");
        if ($lref)
        {
            my @lines=@$lref;
            my $line;
            foreach $line (@lines)
            {
                print "$line\n";
            }
        }
    }
}


##-----
# print the message to stderr and exit
##-----
sub print_fatal()
{
    my $msg=shift;
    print "$msg\n";
    exit(1);
}

sub usage()
{
    print<<EOF;
${me} v${g_version_s}
A script to list/download files from googlecode

Usage: ${me} options
Where the options include:
 --project=name  googlecode project name
 --progress      Show progress
 --quiet         Be quiet
 --getprompt     Prompt for downloading 
 --get=file      Download this this specific file
 --help          Show this help
 --version       Show version information

Example:
  \$ $me --project="testxyz"
  \$ $me --project="testxyz" --get=file.tar.gz
  \$ $me --project="testxyz" --get=file.tar.gz --progress
  \$ $me --project="testxyz" --getprompt
  \$ $me --project="testxyz" --getprompt --progress
  \$ $me --project="testxyz" --getprompt --progress --debug

EOF
;
    exit(0);
}

sub doit()
{
    my $result=GetOptions(%g_options);
    exit(0)  if (!$result);
    &usage() if ($g_help);

    if ($g_version)
    {
        print "$me v${g_version_s}\n";
        exit(0);
    }
    if (! $g_project)
    {
        &print_fatal("Error: Project name must be specified with --project");
    }


    my $curl_args=$g_curl_path;
    if ($g_show_progress)
    {
        $curl_args .=" -#";
    }
    else
    {
        if (!$g_show_progress)
        {
            $curl_args .=" -s";
        }
    }
    my $g_curl_path=$curl_args;


    my $url="http://${g_domain}/p/${g_project}/downloads/list";
    my $cmd="${g_curl_path} ${url}";
    &print_debug("$cmd");
    
    my %urls=();
    my $lref=&run_cmd("$cmd");
    if ($lref)
    {
        my @lines=@$lref;
        my $line;
        my @files=();
        foreach $line (@lines)
        {
            chomp($line);
#            if ($line =~ /<a href=\"(http:\/\/${g_project}.googlecode.com\/files)\/(.*)/)
            if ($line =~ /<a href=\"\/\/(${g_project}.googlecode.com\/files)\/(.+)\" .*$/)
            {
                my $url="$1";
                my $match="$2";
                my ($fname,$junk)=split('\s+',$match);
                $fname =~ s/\"//g;
                &print_debug("base_url: $url");
                &print_debug("file: $fname");
                $urls{"$fname"}="$url";
                push(@files,"$fname");
            }
        }
        if ($g_fetch)
        {
            my $file = $g_fetch;
            print "Download $file\n";
            &fetch_file($g_curl_path,"$file",$urls{"$file"},0);
            exit(0);
        }

        if (scalar(@files))
        {
            print "Project: $g_project\n";
            my $file;
            foreach $file (@files)
            {
                print " $file\n";
                if ($g_fetch_prompt)
                {
                    &fetch_file($g_curl_path,"$file",$urls{"$file"},1);
                }
            }
        }
    }
}

##-----
# print debug message to stderr if debugging is on
##-----
sub print_debug()
{
    my $msg=shift;

    return if (!$g_debug);
    return if (!$msg);

    my $lno=(caller(0))[2];
    my $t=localtime(time());
    print STDERR "$t ($lno): $msg\n";
}

##-----
# prompt for password
##-----
sub my_getpass
{
    my $prompt=shift || "Enter password:";
    my $pass='';
    my $c;
    local *FD;
    return if (! -t STDOUT and ! -t STDIN);

    if (&os_type() eq "unix")
    {
        open (FD,"/dev/tty") or die "Could open /dev/tty $!";
        system("stty -echo");
        system("stty raw"); 
    }
    else
    {
        *FD=*STDIN;
    }

    print STDOUT "$prompt";
    while ($c=getc(FD))
    {
        my $x=unpack("C*",$c);
        if ($x eq '3') # Ctrl-C
        {
            &reset_tty();
            close(FD) if (&os_type() eq "unix");
            return('');
        }
        last if ($c eq "\n");
        last if ($c eq "\r");
        if (&os_type() eq "unix")
        {
            print "*";
        }

        $pass .= $c;
    }


    if (&os_type() eq "unix")
    {
        &reset_tty();
        close(FD);
    }
    return ($pass);
}

##---
# returns "unix" if it is not mswin32
##---
sub os_type()
{
    my $os=$^O;
    &print_debug("OS is: $os");
    if ($os !~ /mswin32/i)
    {
        $os="unix";
    }
    return($os);
}

##-----
# reset tty 
##-----
sub reset_tty()
{   
    if (&os_type() !~ /mswin32/i)
    {
        system("stty sane");
        system("stty echo");
        system("stty erase ^H");
    }
}

